﻿using System;
using System.IO;
using System.Linq;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Lucene.Net.Search;
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using IKAnalyzer.Net.Lucene;
using Ayatta.Contract.Domain;
using System.Collections.Generic;
using Lucene.Net.Analysis.Tokenattributes;
using Ayatta.Service.Product.Index.Domain;
using Ayatta.Service.Product.Index.Extension;
using Lucene.Net.Search.Highlight;

namespace Ayatta.Service.Product.Index
{
    internal abstract class LuceneIndexer<T>
    {
        /// <summary>
        /// 是否已完成初始化
        /// </summary>
        private bool flag;

        /// <summary>
        /// 是否在生成索引过程中
        /// </summary>
        private bool status;

        /// <summary>
        /// 当前索引使用的目录
        /// </summary>
        private IndexDirectory current = IndexDirectory.Master;

        /// <summary>
        /// 线程同步辅助对像
        /// </summary>
        private readonly object lockhelper = new object();

        /// <summary>
        /// 添加索引文件
        /// </summary>
        private readonly Action<IndexDirectory> addDocument;

        /// <summary>
        /// 转换索引文件
        /// </summary>
        private readonly Func<Document, T> convertDocument;

        /// <summary>
        /// Lucene IndexSearcher
        /// </summary>
        public IndexSearcher Searcher { get; private set; }

        /// <summary>
        /// 最后一次生成索引时间
        /// </summary>
        public DateTime LastTime { get; private set; }

        /// <summary>
        ///  Lucene.Net版本号
        /// </summary>
        public const Lucene.Net.Util.Version LuceneVersion = Lucene.Net.Util.Version.LUCENE_30;


        protected LuceneIndexer(Action<IndexDirectory> action, Func<Document, T> func)
        {
            var path = current.Path();
            if (!System.IO.Directory.Exists(path))
            {
                System.IO.Directory.CreateDirectory(path);
                action(current);
            }
            addDocument = action;
            convertDocument = func;
            var directory = FSDirectory.Open(path);
            Searcher = new IndexSearcher(IndexReader.Open(directory, true));
        }

        /// <summary>
        ///  生成索引
        /// </summary>
        public void Index()
        {
            if (status)
            {
                return;
            }
            lock (lockhelper)
            {
                status = true;
            }

            var target = flag ? current.Switch() : IndexDirectory.Slave;

            var async = new AsyncIndexer(addDocument);

            async.BeginIndex(target, o =>
            {
                async.EndIndex(o);
                var path = current.Path();
                var directory = FSDirectory.Open(path);
                var searcher = new IndexSearcher(IndexReader.Open(directory, true));
                lock (lockhelper)
                {
                    LastTime = DateTime.Now;
                    Searcher = searcher;
                    current = target;
                    status = false;
                    flag = true;
                }
            }, null);

        }

        public static IEnumerable<string> Participle(string input, bool distinct = false)
        {
            var array = new List<string>();
            if (!string.IsNullOrEmpty(input))
            {
                using (var reader = new StringReader(input))
                {
                    var tokenizer = new IKTokenizer(reader, false);
                    tokenizer.Reset();
                    while (tokenizer.IncrementToken())
                    {
                        var termAtt = tokenizer.GetAttribute<ITermAttribute>();
                        array.Add(termAtt.Term);
                    }
                }
            }
            return distinct ? array.Distinct() : array;
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="param">查询参数</param>
        /// <returns></returns>
        protected PagedList<T> Query(LuceneQueryParam param)
        {
            if (Searcher == null || param == null)
            {
                return new PagedList<T>();
            }

            var size = param.PageSize;
            var page = param.PageIndex;

            var result = new List<T>();
            var hits = param.Sort == null ? Searcher.Search(param.Query, param.Filter, page * size) : Searcher.Search(param.Query, param.Filter, page * size, param.Sort);

            var count = hits.TotalHits;

            if (count > 0)
            {
                var pageCount = count / size + (count % size > 0 ? 1 : 0);
                page = Math.Min(page, pageCount);

                var startPos = Math.Max((page - 1) * size, 0);
                var endPos = Math.Min(page * size - 1, count - 1);

                for (var i = startPos; i <= endPos; i++)
                {
                    var id = hits.ScoreDocs[i].Doc;
                    var document = Searcher.Doc(id);
                    if (!string.IsNullOrEmpty(param.HighlightField))
                    {
                        var queryScorer = new QueryScorer(param.Query);
                        var formatter = new SimpleHTMLFormatter("<span class='highlight'>", "</span>");
                        var highlighter = new Highlighter(formatter, queryScorer);
                        var fieldName = param.HighlightField;
                        var fieldValue = document.Get(fieldName);
                        var fragment = highlighter.GetBestFragment(new IKAnalyzer.Net.Lucene.IKAnalyzer(), fieldName, fieldValue);
                        if (fragment != null)
                        {
                            document.GetField(fieldName).SetValue(fragment);
                        }
                    }
                    result.Add(convertDocument(document));
                }
            }

            return new PagedList<T>(result, count);
        }

        ///// <summary>
        ///// 查询
        ///// </summary>
        ///// <param name="param">查询参数</param>
        ///// <param name="calculate">计算统计信息</param>
        ///// <returns></returns>
        //protected PagedList<T, TExtra> Query<TExtra>(LuceneQueryParam param, Func<TopDocs, TExtra> calculate)
        //{
        //    if (Searcher == null || param == null)
        //    {
        //        return new PagedList<T, TExtra>();
        //    }

        //    var size = param.PageSize;
        //    var page = param.PageIndex;

        //    var result = new List<T>();
        //    var hits = param.Sort == null ? Searcher.Search(param.Query, param.Filter, page * size) : Searcher.Search(param.Query, param.Filter, page * size, param.Sort);

        //    var count = hits.TotalHits;

        //    if (count > 0)
        //    {
        //        var pageCount = count / size + (count % size > 0 ? 1 : 0);
        //        page = Math.Min(page, pageCount);

        //        var startPos = Math.Max((page - 1) * size, 0);
        //        var endPos = Math.Min(page * size - 1, count - 1);

        //        for (var i = startPos; i <= endPos; i++)
        //        {
        //            var id = hits.ScoreDocs[i].Doc;
        //            var doc = Searcher.Doc(id);
        //            result.Add(convertDocument(doc));
        //        }
        //    }
        //    if (calculate != null)
        //    {
        //        var extra = calculate(hits);
        //        return new PagedList<T, TExtra>(result, count, extra);
        //    }
        //    return new PagedList<T, TExtra>(result, count);
        //}


        #region 内部辅助类
        /// <summary>
        /// 提供异步生成索引类
        /// </summary>
        private class AsyncIndexer
        {
            /// <summary>
            /// 委托
            /// </summary>
            private Action<IndexDirectory> action;

            /// <summary>
            /// 初始化异步委托
            /// </summary>
            internal AsyncIndexer(Action<IndexDirectory> action)
            {
                this.action = action;
            }

            /// <summary>
            /// 异步生成关键字索引开始
            /// </summary>
            /// <param name="target">生成索引目标目录</param>
            /// <param name="callback">回调函数</param>
            /// <param name="state">状态</param>
            /// <returns></returns>
            internal IAsyncResult BeginIndex(IndexDirectory target, AsyncCallback callback, object state)
            {
                if (action == null)
                {
                    throw new ArgumentException("action");
                }
                return action.BeginInvoke(target, callback, state);
            }

            /// <summary>
            /// 异步生成关键字索引结束
            /// </summary>
            /// <param name="asyncResult"></param>
            internal void EndIndex(IAsyncResult asyncResult)
            {

                if (asyncResult == null)
                    throw new ArgumentNullException("asyncResult");
                if (action == null)
                    throw new ArgumentException("action");

                try
                {
                    action.EndInvoke(asyncResult);
                }
                finally
                {
                    action = null;
                }

            }
        }

        #endregion
    }
}